#include <memory/host.h>
#include <memory/paddr.h>
#include <device/mmio.h>
#include <cpu/difftest.h> 

#define word_t  uint64_t
static uint8_t *pmem = NULL;
//#if   defined(CONFIG_PMEM_MALLOC)
//static uint8_t *pmem = NULL;
//#else // CONFIG_PMEM_GARRAY
#define CONFIG_MSIZE 0x8000000
//static uint8_t pmem[CONFIG_MSIZE] PG_ALIGN = {};
//#endif
//-----------------------begin-vga------------------------------------//

#define SCREEN_W (MUXDEF(CONFIG_VGA_SIZE_800x600, 800, 400))
#define SCREEN_H (MUXDEF(CONFIG_VGA_SIZE_800x600, 600, 300))

static uint32_t screen_width() {
  return SCREEN_W;
}

static uint32_t screen_height() {
  return SCREEN_H;
}

static uint32_t screen_size() {
  return screen_width() * screen_height() * sizeof(uint32_t);
}

static void *vmem = NULL;
static uint32_t *vgactl_port_base = NULL;

#include <SDL2/SDL.h>

static SDL_Renderer *renderer = NULL;
static SDL_Texture *texture = NULL;

static void init_screen() {
  SDL_Window *window = NULL;
  char title[128];
  sprintf(title, "%s-NPC", "RISCV64");
  SDL_Init(SDL_INIT_VIDEO);
  SDL_CreateWindowAndRenderer(
      SCREEN_W * 2,
      SCREEN_H * 2,
      0, &window, &renderer);
  SDL_SetWindowTitle(window, title);
  texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
      SDL_TEXTUREACCESS_STATIC, SCREEN_W, SCREEN_H);
}

static inline void update_screen() {
  SDL_UpdateTexture(texture, NULL, vmem, SCREEN_W * sizeof(uint32_t));
  SDL_RenderClear(renderer);
  SDL_RenderCopy(renderer, texture, NULL, NULL);
  SDL_RenderPresent(renderer);
}
void vga_update_screen() {
  // TODO: call `update_screen()` when the sync register is non-zero,
  // then zero out the sync register
  int am_sync = vgactl_port_base[1];
  if(am_sync != 0){
  	update_screen();
	vgactl_port_base[1] = 0;
  }
}

void init_vga() {
  //vgactl_port_base = (uint32_t *)new_space(8);
  //vgactl_port_base[0] = (screen_width() << 16) | screen_height();
  vgactl_port_base = (uint32_t *)malloc(8);
  vgactl_port_base[0] = (screen_width() << 16) | screen_height();

  //vmem = new_space(screen_size());
  vmem = (uint8_t *)malloc(screen_size());
  
  init_screen();
  memset(vmem, 0, screen_size());
}

//-----------------------end-vga-------------------------------------//
//-----------------------begin-timer---------------------------------//
#define DEVICE_BASE 0xa0000000
#define RTC_ADDR        (DEVICE_BASE + 0x0000048)
#include <common.h>
#include <sys/time.h>
static uint64_t boot_time = 0;

static uint64_t get_time_internal() {
  struct timeval now;
  gettimeofday(&now, NULL);
  uint64_t us = now.tv_sec * 1000000 + now.tv_usec;
  return us;
}

uint64_t get_time() {
  if (boot_time == 0) boot_time = get_time_internal();
  uint64_t now = get_time_internal();
  return now - boot_time;
}
//---------------------------end-timer------------------------------//

uint8_t* guest_to_host(paddr_t paddr) { return pmem + paddr - CONFIG_MBASE; }
paddr_t host_to_guest(uint8_t *haddr) { return haddr - pmem + CONFIG_MBASE; }

static word_t pmem_read(paddr_t addr, int len) {
  word_t ret = host_read(guest_to_host(addr), len);
  return ret;
}

static void pmem_write(paddr_t addr, int len, word_t data) {
  host_write(guest_to_host(addr), len, data);
}

static void out_of_bound(paddr_t addr) {
  panic("address = " FMT_PADDR " is out of bound of pmem [" FMT_PADDR ", " FMT_PADDR ") at pc = " ,addr, CONFIG_MBASE, CONFIG_MBASE + CONFIG_MSIZE);
}

void init_mem() {
  pmem = (uint8_t *)malloc(CONFIG_MSIZE);
  Log("physical memory area [" FMT_PADDR ", " FMT_PADDR "]",
      (paddr_t)CONFIG_MBASE, (paddr_t)CONFIG_MBASE + CONFIG_MSIZE);
  init_vga();
}

word_t paddr_read(paddr_t addr, int len) {
/*  #ifdef CONFIG_MTRACE
	if(addr > CONFIG_MBASE+CONFIG_PADDR_START && addr < CONFIG_MBASE+CONFIG_PADDR_END)
	printf("paddr to read:0x%08x, with len %d\n",addr,len);
  #endif*/
  if (likely(in_pmem(addr))) return pmem_read(addr, len);
//  out_of_bound(addr);
  return 0;
}

extern "C" void mpmem_read(long long raddr, long long *rdata) {
 // 总是读取地址为`raddr & ~0x7ull`的8字节返回给`rdata`
   if(likely(in_pmem(raddr+4))){
 	*rdata= pmem_read(raddr,8);

	return ;
  }
  else if(raddr == RTC_ADDR){
	uint64_t us = get_time();
	*rdata = (uint64_t)us;
//        printf("paddr read us %ld\n",*rdata);
  	return;
  }
  else if(raddr == RTC_ADDR+4){
	uint64_t us = get_time();
        printf("paddr read us>>32 %ld\n",us>>32);
	*rdata = us>>32;
	return;
  }
  else{
//	  printf("mpmem_read\n");
//	out_of_bound(raddr);
	return ;
  }

 }




extern "C" void mpmem_write(long long waddr, long long wdata, char wmask) {
// 总是往地址为`waddr & ~0x7ull`的8字节按写掩码`wmask`写入`wdata`
// `wmask`中每比特表示`wdata`中1个字节的掩码,
// 如`wmask = 0x3`代表只写入最低2个字节, 内存中的其它字节保持不变
//	printf("wmask:%d\n",wmask);
if (likely(in_pmem(waddr))){
	switch (wmask){
		case 0xff : case -1: pmem_write(waddr,8,wdata);break;//8 
		case 0x0f : pmem_write(waddr,4,wdata);break;//4
		case 0x03 : pmem_write(waddr,2,wdata);break;//2
		case 0x01 : pmem_write(waddr,1,wdata);break;//1
		default : printf("mask wrong! wmask:%d\n",(int)wmask);
	}
return ;
}
else if(waddr == 0xa00003f8){
	putc(wdata,stderr);
	return ;
}
else if(waddr >= 0xa1000000 && waddr <= 0xa1000000 + screen_size()){
	*(uint32_t *)(vmem + waddr - 0xa1000000) = wdata;
//	printf("vem (%d)write %x in %d\n",screen_size(),waddr,wdata);
	
}
else if(waddr == 0xa0000104){
	vgactl_port_base[1] = wdata;
//	printf("port1 write\n");
}
else{
//	out_of_bound(waddr);
	return ;
}
}




